﻿//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2020 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------

using System;
using System.Collections.Generic;
using Loxodon.Framework;
using Loxodon.Framework.Views;
using Loxodon.Framework.Views.Variables;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
using XLua;

namespace StarForce
{
    [LuaCallCSharp]
    public class UGuiForm : UIFormLogic, ILuaExtendable
    {
        public const int DepthFactor = 10;

        private static Font s_MainFont = null;
        private Canvas m_CachedCanvas = null;
        private CanvasGroup m_CanvasGroup = null;
        private List<Canvas> m_CachedCanvasContainer = new List<Canvas>();

        public int OriginalDepth { get; private set; }

        public int Depth
        {
            get { return m_CachedCanvas.sortingOrder; }
        }

        private bool m_IsDebugLog = false;

        public void EnableLog()
        {
            m_IsDebugLog = true;
        }

        public void DisableLog()
        {
            m_IsDebugLog = false;
        }

        public void FLog(string log)
        {
            if (m_IsDebugLog)
            {
                Log.Debug(log);
            }
        }

        //
        public ScriptReference script;
        public VariableArray variables;

        protected LuaTable scriptEnv;
        protected LuaTable metatable;
        protected Action<MonoBehaviour> onInit;
        protected Action<MonoBehaviour> onPause;
        protected Action<MonoBehaviour> onResume;
        protected Action<MonoBehaviour> onOpen;
        protected Action<MonoBehaviour> onUpdate;
        protected Action<MonoBehaviour> onClose;
        protected Action<MonoBehaviour> onCover;
        protected Action<MonoBehaviour> onReveal;
        protected Action<MonoBehaviour> onRefocus;
        protected Action<MonoBehaviour> onRecycle;
        protected Action<MonoBehaviour> onDestroy;

        public virtual LuaTable GetMetatable()
        {
            return this.metatable;
        }

        protected virtual void Initialize()
        {
            var luaEnv = LuaEnvironment.LuaEnv;
            scriptEnv = luaEnv.NewTable();

            LuaTable meta = luaEnv.NewTable();
            meta.Set("__index", luaEnv.Global);
            scriptEnv.SetMetaTable(meta);
            meta.Dispose();

            scriptEnv.Set("target", this);

            string scriptText = (script.Type == ScriptReferenceType.TextAsset)
                ? script.Text.text
                : string.Format(
                    "require(\"framework.System\");local cls = require(\"{0}\");return extends(target,cls);",
                    script.Filename);
            object[] result = luaEnv.DoString(scriptText, string.Format("{0}({1})", "LuaBehaviour", this.name),
                scriptEnv);

            if (result.Length != 1 || !(result[0] is LuaTable))
                throw new Exception("");

            metatable = (LuaTable) result[0];
            if (variables != null && variables.Variables != null)
            {
                foreach (var variable in variables.Variables)
                {
                    var name = variable.Name.Trim();
                    if (string.IsNullOrEmpty(name))
                        continue;

                    metatable.Set(name, variable.GetValue());
                }
            }

            onInit = metatable.Get<Action<MonoBehaviour>>("init");
            onPause = metatable.Get<Action<MonoBehaviour>>("pause");
            onResume = metatable.Get<Action<MonoBehaviour>>("resume");
            onOpen = metatable.Get<Action<MonoBehaviour>>("open");
            onUpdate = metatable.Get<Action<MonoBehaviour>>("update");
            onClose = metatable.Get<Action<MonoBehaviour>>("close");
            onCover = metatable.Get<Action<MonoBehaviour>>("cover");
            onReveal = metatable.Get<Action<MonoBehaviour>>("reveal");
            onRefocus = metatable.Get<Action<MonoBehaviour>>("refocus");
            onRecycle = metatable.Get<Action<MonoBehaviour>>("recycle");
            onDestroy = metatable.Get<Action<MonoBehaviour>>("destroy");
        }
        //

        public void Close()
        {
            StopAllCoroutines();
            GameEntry.UI.CloseUIForm(this);
        }

        public void PlayUISound(int uiSoundId)
        {
            GameEntry.Sound.PlayUISound(uiSoundId);
        }

        public static void SetMainFont(Font mainFont)
        {
            if (mainFont == null)
            {
                Log.Error("Main font is invalid.");
                return;
            }

            s_MainFont = mainFont;

            GameObject go = new GameObject();
            go.AddComponent<Text>().font = mainFont;
            Destroy(go);
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnInit(object userData)
#else
        protected internal override void OnInit(object userData)
#endif
        {
            base.OnInit(userData);

            m_CachedCanvas = gameObject.GetOrAddComponent<Canvas>();
            m_CachedCanvas.overrideSorting = true;
            OriginalDepth = m_CachedCanvas.sortingOrder;

            m_CanvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();

            RectTransform transform = GetComponent<RectTransform>();
            transform.anchorMin = Vector2.zero;
            transform.anchorMax = Vector2.one;
            transform.anchoredPosition = Vector2.zero;
            transform.sizeDelta = Vector2.zero;
            transform.localPosition = Vector3.zero;

            gameObject.GetOrAddComponent<GraphicRaycaster>();

            Text[] texts = GetComponentsInChildren<Text>(true);
            for (int i = 0; i < texts.Length; i++)
            {
                texts[i].font = s_MainFont;
                // if (!string.IsNullOrEmpty(texts[i].text))
                // {
                //     texts[i].text = GameEntry.Localization.GetString(texts[i].text);
                // }
            }

            this.Initialize();

            if (onInit != null)
            {
                onInit(this);
            }

            FLog($"==>{this.gameObject.name} OnInit");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnRecycle()
#else
        protected internal override void OnRecycle()
#endif
        {
            base.OnRecycle();

            if (onRecycle != null)
            {
                onRecycle(this);
            }
            FLog($"==>{this.gameObject.name} OnRecycle");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnOpen(object userData)
#else
        protected internal override void OnOpen(object userData)
#endif
        {
            base.OnOpen(userData);

            StopAllCoroutines();


            if (onOpen != null)
            {
                onOpen(this);
            }

            FLog($"==>{this.gameObject.name} OnOpen");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnClose(bool isShutdown, object userData)
#else
        protected internal override void OnClose(bool isShutdown, object userData)
#endif
        {
            base.OnClose(isShutdown, userData);

            if (onClose != null)
                onClose(this);

            FLog($"==>{this.gameObject.name} OnClose");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnPause()
#else
        protected internal override void OnPause()
#endif
        {
            base.OnPause();
            if (onPause != null)
            {
                onPause(this);
            }

            FLog($"==>{this.gameObject.name} OnPause");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnResume()
#else
        protected internal override void OnResume()
#endif
        {
            base.OnResume();

            if (onResume != null)
            {
                onResume(this);
            }

            FLog($"==>{this.gameObject.name} OnResume");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnCover()
#else
        protected internal override void OnCover()
#endif
        {
            base.OnCover();
            if (onCover != null)
            {
                onCover(this);
            }

            FLog($"==>{this.gameObject.name} OnCover");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnReveal()
#else
        protected internal override void OnReveal()
#endif
        {
            base.OnReveal();

            if (onReveal != null)
            {
                onReveal(this);
            }

            FLog($"==>{this.gameObject.name} onReveal");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnRefocus(object userData)
#else
        protected internal override void OnRefocus(object userData)
#endif
        {
            base.OnRefocus(userData);
            if (onRefocus != null)
            {
                onRefocus(this);
            }

            FLog($"==>{this.gameObject.name} OnRefocus");
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
#else
        protected internal override void OnUpdate(float elapseSeconds, float realElapseSeconds)
#endif
        {
            base.OnUpdate(elapseSeconds, realElapseSeconds);

            if (onUpdate != null)
            {
                onUpdate(this);
            }
        }

#if UNITY_2017_3_OR_NEWER
        protected override void OnDepthChanged(int uiGroupDepth, int depthInUIGroup)
#else
        protected internal override void OnDepthChanged(int uiGroupDepth, int depthInUIGroup)
#endif
        {
            int oldDepth = Depth;
            base.OnDepthChanged(uiGroupDepth, depthInUIGroup);
            int deltaDepth = UGuiGroupHelper.DepthFactor * uiGroupDepth + DepthFactor * depthInUIGroup - oldDepth +
                             OriginalDepth;
            GetComponentsInChildren(true, m_CachedCanvasContainer);
            for (int i = 0; i < m_CachedCanvasContainer.Count; i++)
            {
                m_CachedCanvasContainer[i].sortingOrder += deltaDepth;
            }

            m_CachedCanvasContainer.Clear();

            FLog($"==>{this.gameObject.name} OnDepthChanged");
        }

        private void OnDestroy()
        {
            if (onDestroy!=null)
            {
                onDestroy(this);
            }

            onClose = null;
            onUpdate = null;
            onOpen = null;
            onPause = null;
            onResume = null;
            onInit = null;
            onCover = null;
            onReveal = null;
            onRefocus = null;
            onDestroy = null;

            if (metatable != null)
            {
                metatable.Dispose();
                metatable = null;
            }

            if (scriptEnv != null)
            {
                scriptEnv.Dispose();
                scriptEnv = null;
            }
            
            FLog($"==>{this.gameObject.name} OnDestroy");
        }
    }
}